#pragma once

#include <functional>
#include <string>
#include <stdio.h>
#define MY_LOG(tag, format, ...) log("[%s] " format "\n", tag, ##__VA_ARGS__)

#ifndef MAX
  #define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif

#ifndef MIN
  #define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif

#ifdef MY_OS_WINDOWS
  #include <winsock2.h>
  #include <ws2tcpip.h>
  #define inet_aton(str,addr) inet_pton(AF_INET, str, addr)
  typedef HANDLE task_handle_t;
  static int fd_is_valid(int fd) {
    char buffer[1];
    recv(fd, buffer, 1, MSG_PEEK);
    return (WSAECONNRESET != WSAGetLastError());
  }
  #define my_delay(ms) Sleep(ms)
#endif

#ifdef MY_OS_LINUX
  #include <fcntl.h>
  #include <errno.h>
  #include <pthread.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #define closesocket close
  typedef pthread_t task_handle_t;
  typedef int SOCKET;
  static int fd_is_valid(int fd) {
    return fcntl(fd, F_GETFD, 0) != -1 || errno != EBADF;
  }
  #define my_delay(ms) sleep(ms/1000)
#endif

bool server(uint16_t port, void *udata
    , std::function<bool(SOCKET accept_fd, void *udata)> onaccept
    , std::function<void(SOCKET listen_fd, void *udata)> onlisten = nullptr);
bool client(const char *hostname, uint16_t port, void *udata
    , std::function<bool(SOCKET fd, void *udata)> callback);

void log(const char *fmt, ...);
void log_screen(const char *fmt, ...);
void log_file(const char *fmt, ...);
void log_out(std::string &out, const char *fmt, ...);
void show_hex(const char *data, int len, int stride = 16);